home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: comp.sources.misc
- organization: Cornell Univ. CS Dept, Ithaca NY
- subject: v07i117: oracle -- anonymous question/answer program
- from: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
- Reply-To: huttar@svax.cs.cornell.edu (Lars Huttar)
-
- Posting-number: Volume 7, Issue 117
- Submitted-by: huttar@svax.cs.cornell.edu (Lars Huttar)
- Archive-name: oracle
-
- [Two potential problems. First, it uses loooooong filenames and will
- therefore have to be rewritten for System V. Second, there is at least one
- place where a system() command is made with "2>&-", ostensibly to send errors
- to /dev/null -- in reality, this CLOSES standard error and can cause programs
- using stdio to dump core, so be warned. ++bsa]
-
- Oracle is a fun sort of 'message base' program.
- The idea is, you run oracle and ask it a question, on any subject.
- Could be advice on how to make some decision, or the meaning of some word,
- or anything. Later, when someone else runs oracle, they will get your
- question and answer it. Oracle will mail the answer back to you, but
- you don't know who it's from. So it's really just an anonymous game of
- questions and answers.
-
- The Makefile tells how to build the program. Notes tells a little about
- the operation of the program, but you don't need to read it to run oracle.
- #! /bin/sh
- # This is a shell archive, meaning:
- # 1. Remove everything above the #! /bin/sh line.
- # 2. Save the resulting text in a file.
- # 3. Execute the file with /bin/sh (not csh) to create the files:
- # oracle.man
- # Notes
- # oracle.c
- # Makefile
- # This archive created: Thu Aug 3 08:21:44 1989
- export PATH; PATH=/bin:$PATH
- if test -f 'oracle.man'
- then
- echo shar: will not over-write existing file "'oracle.man'"
- else
- sed 's/^X//' << \SHAR_EOF > 'oracle.man'
- X.\" @(#)oracle.6 1.0 7/29/89 Lars Huttar
- X.\" <huttar@occs.oberlin.edu>
- X.TH ORACLE 6 "29 July 1989"
- X.SH NAME
- Xoracle \- answer difficult questions
- X.SH SYNOPSIS
- X.B oracle
- X.SH DESCRIPTION
- X.I Oracle
- Xis a program that accept questions and gives answers; if you want
- Xto know how to use it, just run it. Its operation is pretty self-evident.
- XBut if you want to spoil the secret of how it works, read on.
- X.PP
- XEvery time someone types in a question,
- X.I oracle
- Xsaves it in a file in the
- X.I oracle
- Xdirectory. After someone has typed in a question,
- X.I oracle
- Xlooks in this directory for questions from other users.
- XIt takes the oldest one in there and asks the user to answer it.
- XThen it mails this answer, along with a copy of the question, to
- Xthe person who first asked the question.
- X
- X.SH BUGS
- XIt's not much fun if you're the only one playing. If you see any other
- Xbugs, please send them or any comments to:
- X.in +2
- X.nf
- XLars Huttar
- X<huttar@occs.oberlin.edu>
- X<slh6559@oberlin.bitnet>
- X
- SHAR_EOF
- fi # end of overwriting check
- if test -f 'Notes'
- then
- echo shar: will not over-write existing file "'Notes'"
- else
- sed 's/^X//' << \SHAR_EOF > 'Notes'
- XLib directory:
- XORACLEDIR (defined in Makefile) is a
- Xquestion directory with one file per question.
- XFilename is q_username_time_hostname_processid for each file.
- XOne file is called "record" and contains the directory listing.
- XThe command system("ls -rt q_* > record") fills this file.
- XAnother file is "lockfile", whose existence means an oracle process is
- Xusing the directory to the exclusion of others.
- X
- XProcess when somebody runs *oracle*:
- X A. Say "Please type in your question." Save question in a file in
- X question directory, with x_ prefix.
- X B. Lock questions directory.
- X Update "record" file.
- X Get least recent question file -- that is the first file listed
- X in "record" -- which did not come from the current user.
- X Move that file to t_blahblahblah. (t for temp)
- X Unlock directory.
- X C. Print the question and get answer from user.
- X Save to file a_blahblahblah. Mail to original
- X questioner -- system("mail user < a_blablabla").
- X Move the x_ file (created in A) to q_whatever.
- X Remove the t_ and a_ files -- unless the LOG flag is #defined
- X in which case the a_ files are left around.
- X D. Tell user the answer will reach them in a short while. Ask user
- X to encourage friends to Ask The Oracle.
- X
- XPrefix summary:
- X q_ A question file that has been stored by a previous oracle, or
- X is created from the current user's question just before current
- X oracle terminates.
- X
- X t_ A question file from a different user that has been selected
- X to be shown to and answered by the current user.
- X
- X x_ Temporary file to which the question of the current user is written
- X as it is being typed. Gets moved to q_ if everything goes smoothly.
- X
- X a_ Answer file, exactly as the final answer message will be sent
- X to the Other User whose question was selected. So it contains
- X introductory remarks, a copy of the Other User's question,
- X and the answer given by the current user. This answer is written
- X line-by-line as the user types it in.
- X
- X
- XPermissions:
- X All files will be readable and writable only by the owner.
- XThe *oracle* program will have the setuid bit set, so that
- Xusers will "become" the owner while the program is running.
- XIn order to have mail messages appear to be from the owner of oracle,
- Xthe real uid of the process is set to the effective uid just before
- Xmailing occurs.
- X
- X
- X
- XAccessing the directory:
- X Attempt to cd there before anything starts. If we fail, quit.
- X
- XLog:
- X If a certain flag LOG is #defined, q_ and a_ files will
- Xbe left undeleted in the oracle directory.
- X
- X
- XTo do:
- X After the question is entered, the oracle should have the person
- Xconfirm, "Do you still want to send this question to the oracle?"
- X If DEBUG is defined, check upon startup (a) whether the file
- Xargv[0] has the setuid bit set, and (b) whether it has worked (on some
- Xfilesystems it may be ignored for security reasons).
- X Enter cbreak mode so that if the user tries ^C, the program can
- Xexit gracefully, restoring any q_file it might have moved to t_, and
- Xremoving any created files that are to be abandoned (including lockfile,
- Xx_whatever, and a_whatever).
- X Allow a file to be specified as a command-line argument, that oracle
- Xwill read the question from.
- X
- SHAR_EOF
- fi # end of overwriting check
- if test -f 'oracle.c'
- then
- echo shar: will not over-write existing file "'oracle.c'"
- else
- sed 's/^X//' << \SHAR_EOF > 'oracle.c'
- X/*
- X * Copyright 1989 Lars Huttar
- X *
- X * Permission to use, copy, modify, and distribute this software and its
- X * documentation for any purpose and without fee is hereby granted, provided
- X * that the above copyright notice appear in all copies and that both that
- X * copyright notice and this permission notice appear in supporting
- X * documentation, and that the name of Lars Huttar not be used in advertising
- X * or publicity pertaining to distribution of the software without specific,
- X * written prior permission. Lars Huttar makes no representations about the
- X * suitability of this software for any purpose. It is provided "as is"
- X * without express or implied warranty.
- X *
- X * LARS HUTTAR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- X * INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
- X * SHALL LARS HUTTAR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
- X * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- X * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
- X * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
- X * THIS SOFTWARE.
- X *
- X * Author: Lars Huttar, Oberlin College
- X * huttar@occs.oberlin.edu
- X * slh6559@oberlin.bitnet
- X */
- X
- X#include <stdio.h>
- X#include <sys/param.h>
- X /* ^^ defines MAXHOSTNAMELEN */
- X#include <sys/time.h>
- X#include <errno.h>
- X#include <pwd.h>
- X
- X#ifndef L_cuserid
- X#define L_cuserid 50
- X#endif
- Xint errno;
- X
- X#define MAXPNLEN 50 /* That's being generous. */
- Xstatic char progname[MAXPNLEN], loginname[L_cuserid];
- X /* L_cuserid is defined in stdio.h on some systems,
- X and is the max length of login names. */
- Xint lognamlen;
- X
- X
- Xusage()
- X{ fprintf(stderr, "Usage: %s\n", progname);
- X exit(1);
- X}
- X
- Xmain(argc, argv)
- Xint argc;
- Xchar **argv;
- X{ char *ks, *getlogin();
- X struct passwd *kp;
- X
- X#ifdef DEBUG
- X kp=getpwuid(geteuid());
- X printf("euid: %d %s\n", geteuid(), kp->pw_name);
- X#endif
- X
- X if (chdir(ORACLEDIR)) /* Try to go to the directory. */
- X my_error("error in main(): Couldn't chdir to ", ORACLEDIR);
- X
- X if (!(ks = getlogin()))
- X { if (!(kp = getpwuid(getuid())))
- X my_error("error in main(): Couldn't get username.", "");
- X else strcpy(loginname, kp->pw_name);
- X }
- X else strcpy(loginname, ks);
- X lognamlen=strlen(loginname);
- X
- X strncpy(progname, argv[0], MAXPNLEN-1);
- X progname[MAXPNLEN-1]=NULL;
- X
- X if (argc>1) usage();
- X
- X get_question();
- X get_answer();
- X update_records();
- X farewell();
- X
- X
- X}
- X
- X
- X#define MAXDATESIZE 30
- X#define MAXFNSIZE (MAXHOSTNAMELEN+L_cuserid+MAXDATESIZE+6)
- Xstatic char q_filename[MAXFNSIZE], a_filename[MAXFNSIZE];
- X
- X#define MAXLINELEN 141
- X
- X/** This function, get_question(), asks the user to type in a question.
- X ** The question is written line by line to a temporary file x_blahblahblah.
- X */
- X
- Xget_question()
- X{ char line[MAXLINELEN];
- X FILE *outfp;
- X register int lines=0;
- X
- X make_filename();
- X q_filename[0]='x'; /* This means it's a temporary question file. */
- X
- X if (!(outfp=fopen(q_filename, "w")))
- X my_error("error in get_question(): Couldn't open file for writing: ",
- X q_filename);
- X
- X puts("I am the oracle. I can answer any question");
- X puts("in roughly constant time. Please type in");
- X puts("your question. Enter a blank line when");
- X puts("you are finished (don't use ^D!).\n");
- X
- X do
- X { putchar('>'); putchar(' ');
- X if (!fgets(line, MAXLINELEN-1, stdin) || line[0]=='\n') break;
- X lines=1;
- X line[MAXLINELEN-1]=0;
- X fputs(line, outfp);
- X } while (!feof(stdin));
- X
- X clearerr(stdin);
- X fclose(outfp);
- X
- X if (!lines)
- X { puts("Ask a null question, get a null answer.");
- X if (unlink(q_filename))
- X my_error("error in get_question: Couldn't unlink ", q_filename);
- X exit(1);
- X }
- X}
- X
- X
- Xint got_answer=0;
- X
- Xget_answer()
- X{ FILE *recfp, *quesfp, *ansfp;
- X char line[MAXLINELEN];
- X register int lines=0, num;
- X
- X puts("\nHmmm. I'll have to think about that one for a while.");
- X
- X /** Look for a suitable question file. */
- X
- X if (lock_directory()) return;
- X /* Couldn't lock the directory, so don't require an answer. */
- X
- X system("ls -rt q_* > record 2>&-"); /* stderr goes to /dev/null */
- X if (!(recfp=fopen("record", "r"))) /* Open the file containing list of
- X * question files */
- X my_error("in get_answer(): fopen(\"record\", \"r\") gave error.", "");
- X
- X do
- X {
- X fgets(a_filename, MAXFNSIZE, recfp);
- X if (feof(recfp)) break;
- X
- X if (strncmp(a_filename+2, loginname, lognamlen))
- X break; /* found a question from a different user */
- X } while (!feof(recfp));
- X
- X if (feof(recfp)) /* Didn't find a suitable question file. */
- X { fclose(recfp);
- X unlock_directory();
- X return;
- X }
- X fclose(recfp);
- X
- X a_filename[strlen(a_filename)-1]=(char)NULL; /* Take away final newline */
- X
- X { char nufilename[MAXFNSIZE];
- X
- X strcpy(nufilename, a_filename);
- X nufilename[0]='t'; /* Rename it so other oracles will not answer it. */
- X if (rename(a_filename, nufilename))
- X my_error("Error in get_answer() renaming file ", a_filename);
- X }
- X
- X
- X unlock_directory();
- X
- X
- X /** Ask user to answer question. */
- X
- X a_filename[0]='t';
- X if (!(quesfp=fopen(a_filename, "r")))
- X my_error("Error in get_answer() opening file for input: ", a_filename);
- X
- X a_filename[0]='a';
- X if (!(ansfp=fopen(a_filename, "w")))
- X my_error("Error in get_answer() opening file for output: ", a_filename);
- X
- X fputs("The oracle has pondered your question deeply.\n", ansfp);
- X fputs("Your question was:\n\n", ansfp);
- X
- X puts("Meanwhile, I'd like you to answer a question for me:\n");
- X
- X while (!feof(quesfp))
- X { static int i=0;
- X
- X fgets(line, MAXLINELEN, quesfp);
- X if (!feof(quesfp))
- X { fputs("> ", stdout); fputs(line, stdout);
- X fputs("> ", ansfp); fputs(line, ansfp);
- X }
- X if (++i % 15 == 0) get_return("Press RETURN for More--");
- X }
- X fclose(quesfp);
- X puts("\nWhat would you say?\n");
- X fputs("\nAnd in response, thus spake the oracle:\n\n", ansfp);
- X
- X do
- X { putchar(')'); putchar(' ');
- X fgets(line, MAXLINELEN-1, stdin);
- X if (line[0]=='\n' && lines)
- X break;
- X lines++;
- X line[MAXLINELEN-1]=0;
- X fputs(lines%2 ? "} " : "{ ", ansfp); fputs(line, ansfp);
- X } while (!feof(stdin));
- X
- X { static char *objects[]=
- X {"cents", "of your children", "dollars", "big kisses",
- X "years of slavery", "minutes of life", "newt's eyes",
- X "quarts of soy sauce", "cases of root beer"};
- X
- X num=rand()+getpid()+getuid()+getppid();
- X fprintf(ansfp, "\n\nYou owe the oracle %d %s.\n",
- X num%4+2, objects[num%9]);
- X }
- X
- X fclose(ansfp);
- X
- X switch(num%5)
- X { case 1: puts("\nHa! What kind of answer is that?"); break;
- X case 2: puts("\nNow why couldn't I think of that?"); break;
- X case 3: puts("\nOk, good enough. You pass."); break;
- X default: puts("\nThank you! That one's been troubling me.");
- X }
- X
- X got_answer=1;
- X}
- X
- X
- X#define WAITLIMIT 5
- X
- X/** This function, lock_directory, tries to lock the current directory
- X ** by creating a file "lockfile",
- X ** and returns 0 on success, 1 on failure. */
- Xlock_directory()
- X{ register int lockfd, i=0;
- X
- X for (i=0; i<WAITLIMIT; i++)
- X
- X { if ((lockfd=creat("lockfile", 0)) == -1)
- X { if (errno != EACCES)
- X my_error("Error in lock_directory(): creat(\"lockfile\");", "");
- X }
- X else
- X { if (close(lockfd) == -1)
- X my_error("Error in lock_directory(): closing lockfile","");
- X else return(0);
- X }
- X
- X#ifdef DEBUG
- X if (i==0) printf("Waiting for lock... 1");
- X else printf(", %d", i+1);
- X fflush(stdout);
- X#endif
- X sleep(1); /* Is this too long? usleep() may be more appropriate. */
- X }
- X
- X return(1);
- X}
- X
- X
- X/** This function unlocks the current directory. */
- Xunlock_directory()
- X{
- X#ifdef DEBUG
- X get_return("Press RETURN to unlock the directory.");
- X#endif
- X
- X if (unlink("lockfile") == -1)
- X my_error("Error in unlink() while trying to unlock directory.", "");
- X}
- X
- X
- Xupdate_records()
- X{ char command[L_cuserid+MAXFNSIZE+10], olduser[L_cuserid], newqfn[MAXFNSIZE];
- X register int i;
- X
- X strcpy(newqfn, q_filename);
- X newqfn[0]='q';
- X if (rename(q_filename, newqfn))
- X my_error("update_records(): Error renaming file ", newqfn);
- X
- X if (!got_answer) return;
- X
- X for (i=2; a_filename[i]!='_'; i++)
- X olduser[i-2]=a_filename[i];
- X olduser[i-2]=0;
- X
- X if (setreuid(geteuid(), -1)) /* Make message be From the oracle owner. */
- X my_error("Error in update_records(), setreuid();", "");
- X sprintf(command, "mail %s < %s", olduser, a_filename);
- X if (i=system(command))
- X { static char exit_code[5];
- X sprintf(exit_code, "%d", i);
- X my_error("Trouble mailing reply. Exit code: ", exit_code);
- X }
- X
- X
- X#ifndef LOG
- X if (unlink(a_filename)) /* Remove the answer file (which includes
- X a copy of the question)*/
- X my_error("update_records(): Error unlinking ", a_filename);
- X#endif
- X a_filename[0]='t';
- X if (unlink(a_filename)) /* Remove the old question file */
- X my_error("update_records(): Error unlinking ", a_filename);
- X
- X}
- X
- X
- X/** The function make_filename constructs a string of the form
- X ** q_user_date_host_processid and writes it the global string "q_filename". */
- Xmake_filename()
- X{ char hostname[MAXHOSTNAMELEN], date[MAXDATESIZE];
- X long time; /* no see. */
- X
- X int pid;
- X struct timeval tv;
- X struct timezone tz;
- X
- X if (gethostname(hostname, MAXHOSTNAMELEN))
- X my_error("make_filename(): Had trouble getting hostname;", "");
- X
- X
- X pid=getpid(); /* Get the process id. */
- X
- X time=gettimeofday(&tv, &tz); /* Get the number of seconds since
- X that unforgettable New Year's party */
- X if (time == -1) tv.tv_sec=rand(); /* Sure this isn't error-proof... */
- X strncpy(date, ctime(&(tv.tv_sec))+4, 20); /* +4 to avoid day of the week. */
- X date[20]=NULL; space_to__(date); /* Change spaces to _. */
- X
- X sprintf(q_filename, "q_%s_%s_%s_%d", loginname, date, hostname, pid);
- X /* Just trying to make a unique filename. */
- X}
- X
- Xspace_to__(string)
- Xchar *string;
- X{ register char *p;
- X
- X for (p=string; *p; p++)
- X if (*p==' ') *p='_';
- X}
- X
- X
- X
- X/** The following function says goodbye and advertises
- X ** the program. */
- Xfarewell()
- X{
- X puts("\nYour answer (and your bill) will be mailed to you shortly.");
- X puts("Tell all your friends to Ask the Oracle!");
- X}
- X
- X
- Xmy_error(mess1, mess2)
- Xchar *mess1, *mess2;
- X{ printf("%s%s\n", mess1, mess2);
- X
- X#ifdef DEBUG
- X printf("errno: %d\n", errno);
- X#endif
- X
- X/* printf("EACCES %d EBADF %d EAGAIN %d EINTR %d ENOLCK %d\n",/**deleteme*/
- X/* EACCES, EBADF, EAGAIN, EINTR, ENOLCK);/**deleteme*/
- X perror(progname);
- X exit(1);
- X}
- X
- X
- Xget_return(prompt)
- Xchar *prompt;
- X{ fputs(prompt, stdout);
- X while (getchar()!='\n');
- X}
- X
- SHAR_EOF
- fi # end of overwriting check
- if test -f 'Makefile'
- then
- echo shar: will not over-write existing file "'Makefile'"
- else
- sed 's/^X//' << \SHAR_EOF > 'Makefile'
- X# Makefile for oracle
- X
- X# To make this program, first set TARGETDIR to something appropriate.
- X# You may want to change LIBDIR and CFLAGS as well.
- X# Then 'make all'.
- X
- XTARGETDIR = /mu/huttar/oracle
- XLIBDIR = ${TARGETDIR}/oraclelib
- XCFLAGS = -DLOG -DORACLEDIR=\"${LIBDIR}\"
- X# -DDEBUG and -DLOG are optional.
- X# -DDEBUG causes extra diagnostics to be run, and
- X# -DLOG causes old files to be left around.
- X
- X
- Xall: ${TARGETDIR}/oracle ${LIBDIR}
- X
- X
- X${TARGETDIR}/oracle : oracle.c
- X cc -o ${TARGETDIR}/oracle ${CFLAGS} oracle.c
- X chmod u+s ${TARGETDIR}/oracle
- X
- X${LIBDIR} :
- X mkdir ${LIBDIR}
- X
- SHAR_EOF
- fi # end of overwriting check
- # End of shell archive
- exit 0
- --
- Lars Huttar ---------------------------- rhymes exactly with "sparse butter"
- huttar@svax.cs.cornell.edu ---------- The opinions and decisions are my own.
- After Aug 15: huttar@occs.oberlin.edu or slh6559@oberlin.bitnet -- vi rules!
- You can talk the talk, but can you walk the dog?
-
-
-